<?php
/**
 * Basicamente la idea de esta clase es que maneja un array con properties configurables a
 * nivel de clase-modelo de las properties(Por defecto Exts_Db_Table_PropertyTable,
 * que se usa para representar el modelo de la tabla de propiedades)
 *
 *
 * Lo basico para usar un modelo con properties es:
 *
 * MiTablaDeProperties extends Exts_Db_Table_PropertyTable {
 * 		protected $_primary = "property_id";
 * 		protected $_foreignKey = "MiModeloNuevo_id";
 * 		protected $_name = 'MiTablaDeProperties';
 * 		protected $_properties = array(	"unaPropiedad" => "valorPorDefecto",
 * 										"otraPropiedad" => 2,
 * 										"oootraPropiedad" => "valor" );
 *
 * }
 * MiModeloNuevo extends Exts_Db_Table_DynamicFieldsTable {
 * 		protected $_propertyTableClass = "MiTablaDeProperties";
 * 		protected $_name = 'MiTablaDeModeloNuevo';
 * 		protected $_primary = "modelonuevo_id";
 *
 * }
 *
 * -- Uso --
 *
 * $modelo = new MiModeloNuevo();
 * $unaFila = $modelo->createRow();
 * $unaFila->unaPropiedad = "valor";
 * $unaFila->valorDeLaTablaPrimaria = "valor";
 *
 * $unaFila->save();
 *
 * ------- Resultado ---------
 *
 * Esto va a generar 1 fila nueva en MiTablaDeModeloNuevo
 * 	  y va a generar 3 filas nuevas en MiTablaDeProperties asociadas a la primera.
 *    Con los valores por defecto o seteados-
 *
 *
 * -- Uso Query --
 * 	$modelo = new MiModeloNuevo();
 * 	$fila = $modelo->fetchRow($modelo->select()->where ("modelonuevo_id=?", $unId);
 * -- En este momento esta cargada solo la fila principal --
 *  $valor = $fila->otraPropiedad;
 *  -- Ahora la fila cargo las propiedades y las muestra --
 * (en una segunda vuelta de tuerca podria dividirse en una clase que permita propiedades
 *  cualesquiera y que de ahi extienda esta, que tiene restringidas las properties
 *  a una configuración) 
 */



class Exts_Db_Table_Row_DynamicFieldsRow extends Exts_Db_Table_Row_Abstract {
	protected $_propertiesAreLoaded = false;
	protected $_properties;


	/*
	 * __set / __get. Por defecto usa el del padre.
	 * Si la fila no esta reconocida por el padre, tira una excepcion,
	 * en ese caso se revisa si es una property.
	 * */
	public function __set ($key, $value){
		try {
			parent::__set($key, $value);
		}
		catch (Zend_Db_Table_Row_Exception $e) {
			$this->_setProperty($key,$value);
		}
	}
	public function __get ($key){
		try {
			return parent::__get($key);
		}
		catch (Zend_Db_Table_Row_Exception $e){
			return $this->_getProperty($key);
		}
	}

	/*
	 * 	__unset elimina la propiedad del vector de propiedades. 
	 *  Es importante entender que esto no va a eliminar la propiedad del elemento, solo va a sacarla transitoriamente. 
	 *  (En el proximo fetch las propiedades van a estar nuevamente) 
	 * */
	public function __unset($key) {
		try {
			parent::__unset($key);
		}
		catch (Zend_Db_Table_Row_Exception $e) {
			$this->_loadProperties();
			if (!array_key_exists($key, $this->_properties)){
				throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is not in the row");
			}
			unset($this->_properties[$key]);
		}
	}
	
	
	public function __isset($key) {
		return parent::__isset($key) || array_key_exists($key, $this->_properties);
	}

	protected function _setProperty ($key, $value){
		$this->_loadProperties();
		if (!array_key_exists($key, $this->_properties)){
			throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is not in the row");
		}
		$this->_properties[$key]->setValue($value);

	}
	protected function _getProperty ($key){
		$this->_loadProperties();
		if (!array_key_exists($key, $this->_properties)){
			throw new Zend_Db_Table_Row_Exception("Specified column \"$columnName\" is not in the row");
		}
		return $this->_properties[$key]->getValue();
	}

	/* Solo sirve para primarykeys simples. */
	protected function _getPk () {
		if (!is_array($this->_primary)){
			return $this->_primary;
		}
		return $this->_primary[1];
	}
	/*
	 * Al salvar, en caso de que sea una fila nueva, se generan las properties con valores por
	 * defecto en funcion de la configuración de la clase properties asociada. 
	 * En cualquier caso, si las propiedades estan cargadas se salvan todas.
	 * */
	protected function save (){
		if ($this->isTransient()){
			$isTransient = true;
		}
		parent::save();
		if($isTransient){
			$this->_createProperties();
		}
		if($this->_propertiesAreLoaded) {
			foreach($this->_properties as $key => $row){
				$row->save();
			}
		}
	}
	/*
	 * Este metodo se ejecuta en caso del save de un record trasient, o sea, fila nueva.
	 *
	 * */
	protected function _createProperties() {
		$pk = $this->_getPk();
		$this->_properties =  $this->getTable()->getPropertyTableInstance()->getPropertiesMapFor($this->$pk);
		$this->_propertiesAreLoaded = true;
	}

	/*
	 * Las properties son algo que no se suele usar constantemente, por lo que la carga es a demanda.
	 * */
	protected function _loadProperties () {
		if (!$this->_propertiesAreLoaded) {
			$class = $this->getTable()->getPropertyTableClass();
			$loader = new $class();
			$pk = $this->_getPk();
			$properties = $loader->findPropertiesFor($this->$pk);

			$closure = new Exts_Closures_InyectClosure('$x, $result', ' $result[$x->getKey()] = $x');
			$this->_properties = $properties->inyectInto(array(), $closure);
			$this->_propertiesAreLoaded = true;
		}
		return $this;
	}

	
}